Skip to content

fix(flows): replace GET /flows/exists with POST to support URI-unsaflow names#264

Open
Jayant-kernel wants to merge 9 commits intoopenml:mainfrom
Jayant-kernel:main
Open

fix(flows): replace GET /flows/exists with POST to support URI-unsaflow names#264
Jayant-kernel wants to merge 9 commits intoopenml:mainfrom
Jayant-kernel:main

Conversation

@Jayant-kernel
Copy link

Description:

Summary

  • Removes GET /flows/exists/{name}/{external_version} endpoint
  • Adds POST /flows/exists accepting {"name", "external_version"} in the request body
  • Fixes support for flows with URI-unsafe characters in names (e.g. sklearn flows like sklearn.ensemble.AdaBoostClassifier(base_estimator=sklearn.tree.DecisionTreeClassifier)) that were previously broken with the GET approach

Closes #166

Test plan

  • Updated unit tests to call POST /flows/exists with JSON body
  • Updated migration tests — py_api uses POST, php_api keeps GET (legacy)
  • Flow not found still returns 404 with correct detail message
  • Found flow correctly returns flow_id

Jayant Kernel and others added 2 commits March 7, 2026 22:57
… flow names

The GET /flows/exists/{name}/{external_version} endpoint broke for flows
with URI-unsafe characters in their names (e.g. sklearn flows with
parentheses). Replaced with POST /flows/exists accepting name and
external_version in the request body, resolving issue openml#166.

Closes openml#166
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 7, 2026

Walkthrough

The flow existence check was changed from a GET path to POST /flows/exists and now accepts a new Pydantic model FlowExistsBody (fields name, external_version, both with non-empty length constraints). The primary handler flow_exists now takes a FlowExistsBody and performs lookups using body.name/body.external_version. A deprecated GET /flows/exists/{name}/{external_version} endpoint (flow_exists_get) was added that constructs a FlowExistsBody and delegates to the POST handler. Tests were updated to call POST and to assert validation (HTTP 422) for empty fields; responses remain 404 or {"flow_id": <id>}.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 16.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly describes the main change: replacing GET /flows/exists with POST to support URI-unsafe flow names, which matches the primary objective of the PR.
Description check ✅ Passed The description is directly related to the changeset, detailing the removal of GET endpoint, addition of POST endpoint, the problem it solves, and the test plan.
Linked Issues check ✅ Passed The PR fully addresses issue #166 requirements: removes GET endpoint, adds POST /flows/exists accepting JSON body, supports URI-unsafe characters in flow names, and maintains backward compatibility with deprecation.
Out of Scope Changes check ✅ Passed All changes are directly related to the PR objective of migrating from GET to POST for the /flows/exists endpoint; no unrelated modifications detected across the four modified files.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
📝 Coding Plan
  • Generate coding plan for human review comments

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've left some high level feedback:

  • Removing the GET /flows/exists/{name}/{external_version} route entirely may break existing Python API consumers that construct URLs directly; consider keeping the GET route as a thin wrapper around the new POST handler for a deprecation period.
  • Since the request model is just a simple name/version pair, you might want to reuse or place FlowExistsBody in a shared schemas module so it can be consistently referenced by any future endpoints that need the same shape.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- Removing the `GET /flows/exists/{name}/{external_version}` route entirely may break existing Python API consumers that construct URLs directly; consider keeping the GET route as a thin wrapper around the new POST handler for a deprecation period.
- Since the request model is just a simple name/version pair, you might want to reuse or place `FlowExistsBody` in a shared schemas module so it can be consistently referenced by any future endpoints that need the same shape.

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

@Jayant-kernel
Copy link
Author

@PGijsbers
the GET endpoint broke for flows with URI-unsafe characters (e.g. sklearn flows with parentheses in names). Switching to POST with a JSON body handles all flow names safely.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/routers/openml/flows.py (1)

5-18: ⚠️ Potential issue | 🟡 Minor

Reject empty strings in the new request body.

The old path route could not match empty name or external_version, but FlowExistsBody accepts "" for both fields via plain Pydantic str type. This widens the contract and turns requests with empty strings into a DB lookup that returns no match, resulting in a 404 instead of early validation.

Suggested fix
-from pydantic import BaseModel
+from pydantic import BaseModel, Field
...
 class FlowExistsBody(BaseModel):
-    name: str
-    external_version: str
+    name: str = Field(min_length=1)
+    external_version: str = Field(min_length=1)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/routers/openml/flows.py` around lines 5 - 18, FlowExistsBody currently
allows empty strings for name and external_version because both are plain str;
change the model to reject empty values by specifying non-empty constraints
(e.g., use pydantic.constr(min_length=1) or Field(..., min_length=1)) for the
name and external_version fields in the FlowExistsBody class so validation fails
early instead of passing empty strings to the DB lookup in database.flows.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@src/routers/openml/flows.py`:
- Around line 5-18: FlowExistsBody currently allows empty strings for name and
external_version because both are plain str; change the model to reject empty
values by specifying non-empty constraints (e.g., use
pydantic.constr(min_length=1) or Field(..., min_length=1)) for the name and
external_version fields in the FlowExistsBody class so validation fails early
instead of passing empty strings to the DB lookup in database.flows.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 21fd2185-be28-4cb4-ab90-ec25fd991100

📥 Commits

Reviewing files that changed from the base of the PR and between 2f60ac4 and 3ff845f.

📒 Files selected for processing (1)
  • src/routers/openml/flows.py

Jayant Kernel and others added 3 commits March 8, 2026 00:06
…cated wrapper

- Move FlowExistsBody to schemas/flows.py for reusability
- Keep GET /flows/exists/{name}/{version} as a deprecated thin wrapper
  around POST /flows/exists for backward compatibility
- Update test imports accordingly
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
tests/routers/openml/flows_test.py (1)

79-94: Good validation coverage; consider adding a test for URI-unsafe characters.

The empty field validation test is well-structured. However, since the primary motivation for this PR is to support flow names with URI-unsafe characters (e.g., sklearn.ensemble.AdaBoostClassifier(base_estimator=sklearn.tree.DecisionTreeClassifier)), consider adding a test case that explicitly validates this scenario works correctly with the new POST endpoint.

💡 Suggested test case for URI-unsafe flow names
def test_flow_exists_with_uri_unsafe_characters(py_api: TestClient, mocker: MockerFixture) -> None:
    """Verify POST endpoint handles flow names with URI-unsafe characters."""
    unsafe_name = "sklearn.ensemble.AdaBoostClassifier(base_estimator=sklearn.tree.DecisionTreeClassifier)"
    # This would fail with the old GET endpoint due to URI encoding issues
    response = py_api.post(
        "/flows/exists",
        json={"name": unsafe_name, "external_version": "1.0"}
    )
    # Expecting 404 since this flow doesn't exist in test data
    assert response.status_code == HTTPStatus.NOT_FOUND
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/routers/openml/flows_test.py` around lines 79 - 94, Add a new test
function (e.g., test_flow_exists_with_uri_unsafe_characters) that posts to the
"/flows/exists" endpoint using py_api with a URI-unsafe flow name such as
"sklearn.ensemble.AdaBoostClassifier(base_estimator=sklearn.tree.DecisionTreeClassifier)"
and external_version "1.0"; assert the response.status_code is
HTTPStatus.NOT_FOUND (or the expected non-existent-flow status) to verify the
POST endpoint accepts URI-unsafe characters in the request body rather than
failing due to encoding.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@tests/routers/openml/flows_test.py`:
- Around line 79-94: Add a new test function (e.g.,
test_flow_exists_with_uri_unsafe_characters) that posts to the "/flows/exists"
endpoint using py_api with a URI-unsafe flow name such as
"sklearn.ensemble.AdaBoostClassifier(base_estimator=sklearn.tree.DecisionTreeClassifier)"
and external_version "1.0"; assert the response.status_code is
HTTPStatus.NOT_FOUND (or the expected non-existent-flow status) to verify the
POST endpoint accepts URI-unsafe characters in the request body rather than
failing due to encoding.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 4a67d474-55b6-4840-93cd-34a66f961f6c

📥 Commits

Reviewing files that changed from the base of the PR and between 3ff845f and 8183424.

📒 Files selected for processing (3)
  • src/routers/openml/flows.py
  • src/schemas/flows.py
  • tests/routers/openml/flows_test.py

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
tests/routers/openml/flows_test.py (1)

75-109: Consider adding tests for URI-unsafe characters and the deprecated GET endpoint.

Given the PR's primary objective is to support URI-unsafe characters (e.g., sklearn flows with parentheses like sklearn.pipeline.Pipeline(...)), adding an explicit integration test with such characters would validate the fix end-to-end.

Additionally, there's no test coverage for the deprecated GET /flows/exists/{name}/{external_version} endpoint to verify its delegation behavior works correctly.

📝 Suggested test additions
# Test for URI-unsafe characters in POST (validates the fix)
async def test_flow_exists_with_uri_unsafe_chars(flow: Flow, py_api: httpx.AsyncClient) -> None:
    """Verify that flow names with URI-unsafe characters work via POST."""
    # This test would need a fixture flow with parentheses in its name
    name_with_parens = "sklearn.pipeline.Pipeline(steps=...)"
    response = await py_api.post(
        "/flows/exists", json={"name": name_with_parens, "external_version": "1.0"}
    )
    # Expecting 404 since the flow doesn't exist, but importantly NOT a URI encoding error
    assert response.status_code == HTTPStatus.NOT_FOUND


# Test for deprecated GET endpoint delegation
async def test_flow_exists_get_deprecated(flow: Flow, py_api: httpx.AsyncClient) -> None:
    """Verify deprecated GET endpoint still works and delegates correctly."""
    response = await py_api.get(f"/flows/exists/{flow.name}/{flow.external_version}")
    assert response.status_code == HTTPStatus.OK
    assert response.json() == {"flow_id": flow.id}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/routers/openml/flows_test.py` around lines 75 - 109, Add integration
tests covering URI-unsafe characters and the deprecated GET endpoint: add a test
similar to test_flow_exists_with_uri_unsafe_chars that posts to "/flows/exists"
with a name containing parentheses (e.g., "sklearn.pipeline.Pipeline(...)") and
asserts it returns HTTPStatus.NOT_FOUND (ensuring no URI-encoding failure), and
add a test like test_flow_exists_get_deprecated that calls GET
"/flows/exists/{name}/{external_version}" for an existing Flow fixture and
asserts HTTPStatus.OK and the same JSON payload as test_flow_exists ({"flow_id":
flow.id}) to verify delegation; place these alongside the existing tests
(test_flow_exists, test_flow_exists_not_exists,
test_flow_exists_rejects_empty_fields) so they run in the same suite.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@tests/routers/openml/flows_test.py`:
- Around line 75-109: Add integration tests covering URI-unsafe characters and
the deprecated GET endpoint: add a test similar to
test_flow_exists_with_uri_unsafe_chars that posts to "/flows/exists" with a name
containing parentheses (e.g., "sklearn.pipeline.Pipeline(...)") and asserts it
returns HTTPStatus.NOT_FOUND (ensuring no URI-encoding failure), and add a test
like test_flow_exists_get_deprecated that calls GET
"/flows/exists/{name}/{external_version}" for an existing Flow fixture and
asserts HTTPStatus.OK and the same JSON payload as test_flow_exists ({"flow_id":
flow.id}) to verify delegation; place these alongside the existing tests
(test_flow_exists, test_flow_exists_not_exists,
test_flow_exists_rejects_empty_fields) so they run in the same suite.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 34e30bfa-cec0-4f31-8de3-cc7dc9b45ac1

📥 Commits

Reviewing files that changed from the base of the PR and between 8183424 and 82b12ec.

📒 Files selected for processing (3)
  • src/routers/openml/flows.py
  • tests/routers/openml/flows_test.py
  • tests/routers/openml/migration/flows_migration_test.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • tests/routers/openml/migration/flows_migration_test.py

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

On flow names, external versions and URI-safe characters

1 participant